package com.dyc.aop;

import com.dyc.mapper.OperateLogMapper;
import com.dyc.pojo.OperateLog;
import com.dyc.utils.UserContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Aspect
@Component
@Slf4j
public class OperateLogAspect {

    @Autowired
    private OperateLogMapper operateLogMapper;

    @Autowired
    private ObjectMapper objectMapper;  // 需要配置 Jackson

    /**
     * 定义切入点：拦截 controller 包下的所有方法
     */
    @Pointcut("execution(* com.dyc.controller.*.*(..))")
    public void operateLogPointcut() {
    }

    /**
     * 环绕通知实现日志记录
     */
    //@Around("operateLogPointcut()")
    @Around("@annotation(com.dyc.anno.Log)")
    public Object recordOperateLog(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        Throwable throwable = null;

        try {
            result = joinPoint.proceed();
            return result;
        } catch (Throwable t) {
            throwable = t;
            throw t;
        } finally {
            try {
                // 记录日志（无论成功失败）
                long costTime = System.currentTimeMillis() - startTime;
                recordLog(joinPoint, result, throwable, costTime);
            } catch (Exception e) {
                log.error("操作日志记录失败", e);
            }
        }
    }

    /**
     * 构建并保存日志记录
     */
    private void recordLog(ProceedingJoinPoint joinPoint, Object result, Throwable throwable, long costTime) {
        //将日志信息封装到实体类
        OperateLog operateLog = new OperateLog();

        // 1. 设置操作人（根据项目实际情况调整）
        Integer currentUserId = getCurrentUserId();
        operateLog.setOperateEmpId(currentUserId);

        // 2. 基本信息
        operateLog.setOperateTime(LocalDateTime.now());
        operateLog.setClassName(joinPoint.getTarget().getClass().getName());
        operateLog.setMethodName(joinPoint.getSignature().getName());

        // 3. 方法参数（过滤敏感参数）
        Object[] args = joinPoint.getArgs();
        operateLog.setMethodParams(parseArgs(args));

        // 4. 返回值/异常
        if (throwable != null) {
            operateLog.setReturnValue(throwable.getClass().getName() + ": " + throwable.getMessage());
        } else {
            operateLog.setReturnValue(parseReturnValue(result));
        }

        // 5. 耗时
        operateLog.setCostTime(costTime);

        // 保存日志
        operateLogMapper.insert(operateLog);
    }

    /**
     * 序列化方法参数（过滤不可序列化参数）
     */
    private String parseArgs(Object[] args) {
        try {
            List<Object> safeArgs = new ArrayList<>();
            for (Object arg : args) {
                if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                    safeArgs.add(arg.getClass().getName());
                } else {
                    safeArgs.add(arg);
                }
            }
            return objectMapper.writeValueAsString(safeArgs);
        } catch (JsonProcessingException e) {
            return "参数序列化失败";
        }
    }

    /**
     * 序列化返回值（处理大对象）
     */
    private String parseReturnValue(Object returnValue) {
        try {
            if (returnValue == null) return null;
            return objectMapper.writeValueAsString(returnValue);
        } catch (JsonProcessingException e) {
            return "返回值序列化失败";
        }
    }

    /**
     * 获取当前用户ID（示例实现）
     */
    private Integer getCurrentUserId() {
        // 1. 从 Spring Security 上下文获取

        // 2. 从 ThreadLocal 获取
        return UserContext.getUserId();

        // 3. 从 JWT Token 解析
    }
}